#ifndef __tree_h
#define __tree_h

typedef struct EXPRESSION {
	int lineno;
	enum {
		timesK,
		divK,
		plusK,
		minusK,
		orK,
		andK,
		leqK,
		geqK,
		leK,
		grK,
		eqK,
		neqK,
		termK
	} kind;
	struct TYPE *type;
	union {
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_times;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_div;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_plus;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_minus;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_or;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_and;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_leq;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_geq;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_lessthan;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_greaterthan;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXP_eq;
		struct {
			struct EXPRESSION *left;
			struct EXPRESSION *right;
		} EXPneq;
		struct TERM *term;
	} val;
} EXPRESSION;

typedef struct FUNCTION {
	int lineno;
	struct HEAD *head;
	struct BODY *body;
	struct TAIL *tail;
	/* New addition might have to add Symbol.h in the top*/
	struct SymbolTable *table;
} FUNCTION;

typedef struct HEAD {
	int lineno;
	char *id;
	struct PAR_DECL_LIST *par_decl_list;
	struct TYPE *type;
} HEAD;

typedef struct TAIL {
	int lineno;
	char *id;
} TAIL;

typedef struct TYPE {
	int lineno;
	enum {
		type_idK, intK, boolK, arrayK, recordK
	} kind;
	union {
		char *id;
		struct TYPE *type;
		struct {
			struct VAR_DECL_LIST *var_decl_list;
			struct SymbolTable *table;
		} recordE;
	} val;
} TYPE;

typedef struct PAR_DECL_LIST {
	int lineno;
	struct VAR_DECL_LIST *var_decl_list;
} PAR_DECL_LIST;

typedef struct VAR_DECL_LIST {
	int lineno;
	enum {
		var_decl_listK, var_decl_list_baseK
	} kind;
	struct VAR_DECL_LIST *var_decl_list;
	struct VAR_TYPE *var_type;
} VAR_DECL_LIST;

typedef struct VAR_TYPE {
	int lineno;
	char *id;
	struct TYPE *type;
} VAR_TYPE;

typedef struct BODY {
	int lineno;
	struct DECL_LIST *decl_list;
	struct STATEMENT_LIST *statement_list;
} BODY;

typedef struct DECL_LIST {
	int lineno;
	struct DECL_LIST *decl_list;
	struct DECLARATION *declaration;
} DECL_LIST;

typedef struct DECLARATION {
	int lineno;
	enum {
		typeidK, funcK, varK
	} kind;
	union {
		struct {
			char *id;
			struct TYPE *type;
		} DECL_TYPE;
		struct FUNCTION *function;
		struct VAR_DECL_LIST *var_decl_list;
	} val;
} DECLARATION;

typedef struct STATEMENT_LIST {
	int lineno;
	enum {
		statement_list_baseK, statement_listK
	} kind;
	struct STATEMENT *statement;
	struct STATEMENT_LIST *statement_list;
} STATEMENT_LIST;

typedef struct STATEMENT {
	int lineno;
	enum {
		returnK, writeK, allocateK, variableK, ifK, whileK, statementlistK
	} kind;
	union {
		struct EXPRESSION *return_expression;
		struct EXPRESSION *write_expression;
		struct {
			struct VARIABLE *variable;
			struct OPT_LENGTH *opt_length;
		} allocateE;
		struct {
			struct VARIABLE *variable;
			struct EXPRESSION *expression;
		} variableE;
		struct {
			struct EXPRESSION *expression;
			struct STATEMENT *statement;
			struct OPT_ELSE *opt_else;
		} ifE;
		struct {
			struct EXPRESSION *expression;
			struct STATEMENT *statement;
		} whileE;
		struct STATEMENT_LIST *statement_list;
	} val;
} STATEMENT;

typedef struct OPT_LENGTH {
	int lineno;
	struct EXPRESSION *expression;
} OPT_LENGTH;

typedef struct OPT_ELSE {
	int lineno;
	struct STATEMENT *statement;
} OPT_ELSE;

typedef struct VARIABLE {
	int lineno;
	enum {
		var_idK, var_expK, var_dotK
	} kind;
	struct TYPE *type;
	union {
		char *id;
		struct {
			struct VARIABLE *variable;
			struct EXPRESSION *expression;
		} var_expE;
		struct {
			struct VARIABLE *variable;
			char *id;
		} var_dotE;
	} val;
} VARIABLE;

typedef struct TERM {
	int lineno;
	enum {
		term_varK,
		term_idK,
		term_expK,
		term_notK,
		term_act_expK,
		term_numK,
		term_trueK,
		term_falseK,
		term_nullK
	} kind;
	struct TYPE *type;
	union {
		struct VARIABLE *variable;
		struct {
			char *id;
			struct ACT_LIST *act_list;
		} term_idE;
		struct EXPRESSION *expression;
		struct TERM *term;
		struct EXPRESSION *act_expression;
		int number;
		char *bn;
	} val;
} TERM;

typedef struct ACT_LIST {
	int lineno;
	struct EXP_LIST *exp_list;
} ACT_LIST;

typedef struct EXP_LIST {
	int lineno;
	enum {
		exp_list_baseK, exp_listK
	} kind;
	struct EXPRESSION *expression;
	struct EXP_LIST *exp_list;
} EXP_LIST;

FUNCTION *makeFUNC(HEAD *head, BODY *body, TAIL *tail);
HEAD *makeHEAD(char *id, PAR_DECL_LIST *par_decl_list, TYPE *type);
BODY *makeBODY(DECL_LIST *decl_list, STATEMENT_LIST *statement_list);
TAIL *makeTAIL(char *id);
TYPE *makeTYPE_id(char *id);
TYPE *makeTYPE_intconst();
TYPE *makeTYPE_bool();
TYPE *makeTYPE_array(TYPE *type);
TYPE *makeTYPE_record(VAR_DECL_LIST *var_decl_list);
PAR_DECL_LIST *makePAR_DECL_LIST(VAR_DECL_LIST *var_decl_list);
PAR_DECL_LIST *makeNO_PAR_DECL_LIST();
VAR_DECL_LIST *makeVAR_DECL_LIST(VAR_DECL_LIST *var_decl_list,
		VAR_TYPE *var_type);
VAR_DECL_LIST *makeVAR_DECL_LIST_BASE(VAR_TYPE *var_type);
VAR_TYPE *makeVAR_TYPE(char *id, TYPE *type);
DECL_LIST *makeDECL_LIST(DECL_LIST *decl_list, DECLARATION *declaration);
DECLARATION *makeDECL_id(char *id, TYPE *type);
DECLARATION *makeDECL_func(FUNCTION *function);
DECLARATION *makeDECL_varlist(VAR_DECL_LIST *var_decl_list);
STATEMENT_LIST *makeSTAT_LIST_BASE(STATEMENT *statement);
STATEMENT_LIST *makeSTAT_LIST(STATEMENT_LIST *statement_list,
		STATEMENT *statement);
STATEMENT *makeSTAT_RETURN(EXPRESSION *return_expression);
STATEMENT *makeSTAT_WRITE(EXPRESSION *write_expression);
STATEMENT *makeSTAT_ALLOCATE(VARIABLE *variable, OPT_LENGTH *opt_length);
STATEMENT *makeSTAT_VAR(VARIABLE *variable, EXPRESSION *expression);
STATEMENT *makeSTAT_IF(EXPRESSION *expression, STATEMENT *statement,
		OPT_ELSE *opt_else);
STATEMENT *makeSTAT_WHILE(EXPRESSION *expression, STATEMENT *statement);
STATEMENT *makeSTAT_STAT_LIST(STATEMENT_LIST *statement_list);
OPT_LENGTH *makeLENGTH(EXPRESSION *expression);
OPT_ELSE *makeELSE(STATEMENT *statement);
VARIABLE *makeVAR_id(char *id);
VARIABLE *makeVAR_exp(VARIABLE *variable, EXPRESSION *expression);
VARIABLE *makeVAR_dot(VARIABLE *variable, char *id);
EXPRESSION *makeEXP_eq(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_neq(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_leq(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_geq(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_and(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_or(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_plus(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_minus(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_lessthan(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_greaterthan(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_times(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_div(EXPRESSION *left, EXPRESSION *right);
EXPRESSION *makeEXP_term(TERM *term);
TERM *makeTERM_var(VARIABLE *variable);
TERM *makeTERM_id(char *id, ACT_LIST *act_list);
TERM *makeTERM_exp(EXPRESSION *expression);
TERM *makeTERM_not(TERM *term);
TERM *makeTERM_act_exp(EXPRESSION *expression);
TERM *makeTERM_num(int num);
TERM *makeTERM_true();
TERM *makeTERM_false();
TERM *makeTERM_null();
ACT_LIST *makeACT_LIST(EXP_LIST *exp_list);
EXP_LIST *makeEXP_LIST_BASE(EXPRESSION *expression);
EXP_LIST *makeEXP_LIST(EXP_LIST *exp_list, EXPRESSION *expression);
#endif
